/**
 * 数独算法
 */

const shudu = new Array(9).fill(0).map(() => new Array(9).fill(0));
const hole = new Array(9).fill(0).map(() => new Array(9).fill(0));

// 初始化中间的九宫格
function shudu_1() {
	let a = []; //初始化数组
	let n = 0;
	while (n < 9) {
		let m = Math.floor(Math.random() * 9) + 1;
		let flag = 0;
		for (let i = 0; i < n + 1; i++) {
			if (a[i] == m) {
				flag = 1; //如果之前保存的数中出现了和m相同的数，就把flag标记为1并跳出循环，表示需要重新生成随机数m
				break;
			}
		}
		if (flag == 0) {
			a[n] = m; //如果flag是0，表示前n个数中没有和m相同的数，因此可以把第n+1个元素赋值为m
			n++;
		}
		let k = 0;
		for (let i = 3; i < 6; ++i)
			for (let j = 3; j < 6; ++j)
				shudu[i][j] = a[k++];
	}
}

// 由中间的九宫格交叉变换，初始化上下左右四个九宫格 (交换行列)
function shudu_2() {
	for (let i = 3; i < 6; ++i) {
		let l = 0;
		for (let j = 3; j < 6; ++j) {
			if (i == 3) {
				shudu[i + 1][l] = shudu[i][j];
				shudu[i + 2][l + 6] = shudu[i][j];
				l++;
			} else if (i == 4) {
				shudu[i + 1][l] = shudu[i][j];
				shudu[i - 1][l + 6] = shudu[i][j];
				l++;
			} else if (i == 5) {
				shudu[i - 2][l] = shudu[i][j];
				shudu[i - 1][l + 6] = shudu[i][j];
				l++;
			}
		}
	}
	for (let j = 3; j < 6; ++j) {
		let l = 0;
		for (let i = 3; i < 6; ++i) {
			if (j == 3) {
				shudu[l][j + 1] = shudu[i][j];
				shudu[l + 6][j + 2] = shudu[i][j];
				l++;
			} else if (j == 4) {
				shudu[l][j + 1] = shudu[i][j];
				shudu[l + 6][j - 1] = shudu[i][j];
				l++;
			} else if (j == 5) {
				shudu[l][j - 2] = shudu[i][j];
				shudu[l + 6][j - 1] = shudu[i][j];
				l++;
			}
		}
	}
}

function shudu_3() {
	for (let i = 0; i < 3; ++i) {
		let l = 0;
		for (let j = 3; j < 6; ++j) {
			if (i == 0) {
				shudu[i + 1][l] = shudu[i][j];
				shudu[i + 2][l + 6] = shudu[i][j];
				l++;
			} else if (i == 1) {
				shudu[i + 1][l] = shudu[i][j];
				shudu[i - 1][l + 6] = shudu[i][j];
				l++;
			} else if (i == 2) {
				shudu[i - 2][l] = shudu[i][j];
				shudu[i - 1][l + 6] = shudu[i][j];
				l++;
			}
		}
	}
	for (let i = 6; i < 9; ++i) {
		let l = 0;
		for (let j = 3; j < 6; ++j) {
			if (i == 6) {
				shudu[i + 1][l] = shudu[i][j];
				shudu[i + 2][l + 6] = shudu[i][j];
				l++;
			} else if (i == 7) {
				shudu[i + 1][l] = shudu[i][j];
				shudu[i - 1][l + 6] = shudu[i][j];
				l++;
			} else if (i == 8) {
				shudu[i - 2][l] = shudu[i][j];
				shudu[i - 1][l + 6] = shudu[i][j];
				l++;
			}
		}
	}
}

function dighole(difficulty) {
	var list = [];
	shudu.map(ele => {
		let child = [];
		ele.map(item => {
			child.push({
				type: 1,
				value: item
			})
		})
		list.push({
			children: child
		})
	})
	difficulty *= 20;
	while (difficulty--) {
		list[Math.floor(Math.random() * 9)].children[Math.floor(Math.random() * 9)].type = 2;
	}
	return list;
}

async function generateSudoku(difficulty) {
	await shudu_1();
	await shudu_2();
	await shudu_3();
	return dighole(difficulty);
}


/**
 * 24点游戏算法
 */

function createNums() {
	let data = [],
		i = 0;

	while (i < 4) {
		let num = Math.floor(Math.random() * 8) + 1;
		data.push(num);
		i++;
	}
	return data;
}

function count24() {
	const data = createNums();
	return {
		title: data,
		answer: getResult(getGroup(new dataStruct(...data)))
	};
}

var opt = ['+', '-', '*', '/'], //运算
	dataStruct = function(a, b, c, d) {
		//构建特殊数据结构
		this[1] = a;
		this[2] = b;
		this[4] = c;
		this[8] = d;
	},
	getGroup = function(data) {
		//对dataStruct结构的数字进行排列组合
		var group = [],
			repeat = '';
		try {
			for (var i1 = 1; i1 <= 8; i1 *= 2) {
				for (var i2 = 1; i2 <= 8; i2 *= 2) {
					for (var i3 = 1; i3 <= 8; i3 *= 2) {
						for (var i4 = 1; i4 <= 8; i4 *= 2) {
							if ((i1 | i2 | i3 | i4) != 0xf) continue;
							var str = "" + data[i1] + data[i2] + data[i3] + data[i4];
							//过滤重复组合
							if (repeat.indexOf(str) > -1) continue;
							repeat += str + ",";
							group.push([data[i1], data[i2], data[i3], data[i4]]);
						}
					}
				}
			}
		} catch (e) {
			throw e.message;
		}
		return group;
	};

function operate(f, m, n) {
	//简单的计算函数，正常情况返回计算结果，异常情况返回 NaN
	if (isNaN(m) || isNaN(n)) return NaN;
	if (f == '*') return (m * n);
	else if (f == '/') return n || Number.isInteger((m / n)) ? (m / n) : NaN; //如果除数为0，或者结果为小数则返回 NaN
	else if (f == '-') return (m - n) > 0 ? (m - n) : (n - m);
	else return (parseFloat(m) + parseFloat(n));
}

function compute(a, b, c, d, opt1, opt2, opt3) {
	///获取一组数字的计算结果
	///abcd为4个计算数
	///opt1,opt2,opt3为这四个数依次的运算符号
	var result = []; //定义数组保存计算结果
	try {
		//开始根据5种结合方式进行计算

		//第一种：{[(A,B)C]D}
		var r1 = operate(opt1, a, b);
		var r2 = operate(opt2, r1, c);
		var r3 = operate(opt3, r2, d);
		if (!isNaN(r3) && Math.abs((r3 - 24)) < 1e-5) { //由于计算结果可能出现浮点数，这里的比较必须使用浮点数比较方式
			// result.push('[(' + a + opt1 + b + ')' + opt2 + c + ']' + opt3 + d);
			result.push({
				numsRes: [a, b, r1, c, r2, d],
				formulaRes: '[(' + a + opt1 + b + ')' + opt2 + c + ']' + opt3 + d,
				result: [{
					title: '第一步',
					nums: [a, opt1, b, '=', r1],
					formula: a + opt1 + b,
					res: r1
				}, {
					title: '第二步',
					nums: [r1, opt2, c, '=', r2],
					formula: r1 + opt2 + c,
					res: r2
				}, {
					title: '第三步',
					nums: [r2, opt3, d, '=', r3],
					formula: r2 + opt3 + d,
					res: r3
				}]
			})
		}

		//第二种 {[A(B,C)]D}
		r1 = operate(opt1, b, c);
		r2 = operate(opt2, a, r1);
		r3 = operate(opt3, r2, d);
		if (!isNaN(r3) && Math.abs((r3 - 24)) < 1e-5) {
			// result.push('[' + a + opt2 + '(' + b + opt1 + c + ')]' + opt3 + d);
			result.push({
				numsRes: [b, c, a, r1, r2, d],
				formulaRes: '[' + a + opt2 + '(' + b + opt1 + c + ')]' + opt3 + d,
				result: [{
					title: '第一步',
					nums: [b, opt1, c, '=', r1],
					formula: b + opt1 + c,
					res: r1
				}, {
					title: '第二步',
					nums: [a, opt2, r1, '=', r2],
					formula: a + opt2 + r1,
					res: r2
				}, {
					title: '第三步',
					nums: [r2, opt3, d, '=', r3],
					formula: r2 + opt3 + d,
					res: r3
				}]
			})
		}

		//第三种 {A[(B,C)D]}
		r1 = operate(opt1, b, c);
		r2 = operate(opt2, r1, d);
		r3 = operate(opt3, a, r2);
		if (!isNaN(r3) && Math.abs((r3 - 24)) < 1e-5) {
			// result.push(a + opt3 + '[(' + b + opt1 + c + ')' + opt2 + d + ']');
			result.push({
				numsRes: [b, c, r1, d, a, r2],
				formulaRes: a + opt3 + '[(' + b + opt1 + c + ')' + opt2 + d + ']',
				result: [{
					title: '第一步',
					nums: [b, opt1, c, '=', r1],
					formula: b + opt1 + c,
					res: r1
				}, {
					title: '第二步',
					nums: [r1, opt2, d, '=', r2],
					formula: r1 + opt2 + d,
					res: r2
				}, {
					title: '第三步',
					nums: [a, opt3, r2, '=', r3],
					formula: a + opt3 + r2,
					res: r3
				}]
			})
		}

		//第四种 {A[B(C,D)]}
		r1 = operate(opt1, c, d);
		r2 = operate(opt2, b, r1);
		r3 = operate(opt3, a, r2);
		if (!isNaN(r3) && Math.abs((r3 - 24)) < 1e-5) {
			// result.push(a + opt3 + '[' + b + opt2 + '(' + c + opt1 + d + ')]');
			result.push({
				numsRes: [c, d, b, r1, a, r2],
				formulaRes: a + opt3 + '[' + b + opt2 + '(' + c + opt1 + d + ')]',
				result: [{
					title: '第一步',
					nums: [c, opt1, d, '=', r1],
					formula: c + opt1 + d,
					res: r1
				}, {
					title: '第二步',
					nums: [b, opt2, r1, '=', r2],
					formula: b + opt2 + r1,
					res: r2
				}, {
					title: '第三步',
					nums: [a, opt3, r2, '=', r3],
					formula: a + opt3 + r2,
					res: r3
				}]
			})
		}

		//第五种 [(A,B),(C,D)]
		r1 = operate(opt1, a, b);
		r2 = operate(opt2, c, d);
		r3 = operate(opt3, r1, r2);
		if (!isNaN(r3) && Math.abs((r3 - 24)) < 1e-5) {
			// result.push('(' + a + opt1 + b + ')' + opt3 + '(' + c + opt2 + d + ')');
			result.push({
				numsRes: [a, b, c, d, r1, r2],
				formulaRes: '(' + a + opt1 + b + ')' + opt3 + '(' + c + opt2 + d + ')',
				result: [{
					title: '第一步',
					nums: [a, opt1, b, '=', r1],
					formula: a + opt1 + b,
					res: r1
				}, {
					title: '第二步',
					nums: [c, opt2, d, '=', r2],
					formula: c + opt2 + d,
					res: r2
				}, {
					title: '第三步',
					nums: [r1, opt3, r2, '=', r3],
					formula: r1 + opt3 + r2,
					res: r3
				}]
			})
		}

	} catch (e) {}
	return result;
}

function getResult(group) {
	var result = [],
		repeat = '';
	for (var g = 0; g < group.length; g++) {
		for (var i = 0; i < 4; i++) {
			for (var j = 0; j < 4; j++) {
				for (var k = 0; k < 4; k++) {
					var a1 = group[g][0],
						a2 = group[g][1],
						a3 = group[g][2],
						a4 = group[g][3];

					var tmp = compute(a1, a2, a3, a4, opt[i], opt[j], opt[k]);
					if (tmp.length > 0) {
						for (var t = 0; t < tmp.length; t++) {
							//简单去重
							if (repeat.indexOf(tmp[t].formulaRes) > -1) {
								continue;
							}
							result.push({
								title: `第${result.length + 1}解`,
								result: tmp
							});
							repeat += tmp[t].formulaRes + ',';
						}
					}
				}
			}
		}
	}
	return result;
}

export default {
	generateSudoku,
	count24
}